SPM: Implement SPCI open/close handle SMCs
authorAntonio Nino Diaz <[email protected]>
Thu, 8 Nov 2018 14:20:07 +0000 (14:20 +0000)
committerAntonio Nino Diaz <[email protected]>
Tue, 11 Dec 2018 13:45:41 +0000 (13:45 +0000)
Introduce SMCs that open and close handles according to the SPCI
specification.

Change-Id: I65f365f15612e01aa445e783e96e48ae275c39fd
Signed-off-by: Antonio Nino Diaz <[email protected]>
include/plat/arm/common/arm_spm_def.h
services/std_svc/spm/spci.c
services/std_svc/spm/spm_main.c
services/std_svc/spm/spm_private.h

index 630a29c90178ef25b45a433f59c15a0b0e584265..5c7ca90aba901cf7f771068a651d7462b52ca82b 100644 (file)
 #define PLAT_SPM_NOTIFICATIONS_MAX     U(30)
 #define PLAT_SPM_SERVICES_MAX          U(30)
 
+#define PLAT_SPCI_HANDLES_MAX_NUM      U(20)
+
 #endif /* ARM_SPM_DEF_H */
index 603523f18e9fb7a8965d318911a2f45dbf5d9d7a..cb1b8fdea3d2bb8f4f7d5c14a1b8f28277de036e 100644 (file)
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
+#include <assert.h>
 #include <debug.h>
 #include <smccc.h>
 #include <smccc_helpers.h>
 #include <spci_svc.h>
+#include <spinlock.h>
+#include <string.h>
 #include <utils.h>
 
 #include "spm_private.h"
 
+/*******************************************************************************
+ * Macros to print UUIDs.
+ ******************************************************************************/
+#define PRINT_UUID_FORMAT      "%08x-%08x-%08x-%08x"
+#define PRINT_UUID_ARGS(x)     x[0], x[1], x[2], x[3]
+
+/*******************************************************************************
+ * Array of structs that contains information about all handles of Secure
+ * Services that are currently open.
+ ******************************************************************************/
+typedef enum spci_handle_status {
+       HANDLE_STATUS_CLOSED = 0,
+       HANDLE_STATUS_OPEN,
+} spci_handle_status_t;
+
+typedef struct spci_handle {
+       /* 16-bit value used as reference in all SPCI calls */
+       uint16_t handle;
+
+       /* Client ID of the client that requested the handle */
+       uint16_t client_id;
+
+       /* Current status of the handle */
+       spci_handle_status_t status;
+
+       /*
+        * Context of the Secure Partition that provides the Secure Service
+        * referenced by this handle.
+        */
+       sp_context_t *sp_ctx;
+
+       /*
+        * The same handle might be used for multiple requests, keep a reference
+        * counter of them.
+        */
+       unsigned int num_active_requests;
+} spci_handle_t;
+
+static spci_handle_t spci_handles[PLAT_SPCI_HANDLES_MAX_NUM];
+static spinlock_t spci_handles_lock;
+
+/*
+ * Given a handle and a client ID, return the element of the spci_handles
+ * array that contains the information of the handle. It can only return open
+ * handles. It returns NULL if it couldn't find the element in the array.
+ */
+static spci_handle_t *spci_handle_info_get(uint16_t handle, uint16_t client_id)
+{
+       size_t i;
+
+       for (i = 0; i < ARRAY_SIZE(spci_handles); i++) {
+               spci_handle_t *h = &(spci_handles[i]);
+
+               /* Only check for open handles */
+               if (h->status == HANDLE_STATUS_CLOSED) {
+                       continue;
+               }
+
+               /* Check if either the handle or the client ID are different */
+               if ((h->handle != handle) || (h->client_id != client_id)) {
+                       continue;
+               }
+
+               return h;
+       }
+
+       return NULL;
+}
+
+/*
+ * Returns a unique value for a handle. This function must be called while
+ * spci_handles_lock is locked. It returns 0 on success, -1 on error.
+ */
+static int spci_create_handle_value(uint16_t *handle)
+{
+       /*
+        * Trivial implementation that relies on the fact that any handle will
+        * be closed before 2^16 more handles have been opened.
+        */
+       static uint16_t handle_count;
+
+       *handle = handle_count;
+
+       handle_count++;
+
+       return 0;
+}
+
+/*******************************************************************************
+ * This function looks for a Secure Partition that has a Secure Service
+ * identified by the given UUID. It returns a handle that the client can use to
+ * access the service, and an SPCI_*** error code.
+ ******************************************************************************/
+static uint64_t spci_service_handle_open_poll(void *handle, u_register_t x1,
+                       u_register_t x2, u_register_t x3, u_register_t x4,
+                       u_register_t x5, u_register_t x6, u_register_t x7)
+{
+       unsigned int i;
+       sp_context_t *sp_ptr;
+       uint16_t service_handle;
+
+       /* Bits 31:16 of w7 are reserved (MBZ). */
+       assert((x7 & 0xFFFF0000U) == 0);
+
+       uint16_t client_id = x7 & 0x0000FFFFU;
+       uint32_t uuid[4] = { x1, x2, x3, x4 };
+
+       /* Get pointer to the Secure Partition that handles this service */
+       sp_ptr = spm_sp_get_by_uuid(&uuid);
+       if (sp_ptr == NULL) {
+               WARN("SPCI: Service requested by client 0x%04x not found\n",
+                    client_id);
+               WARN("SPCI:   UUID: " PRINT_UUID_FORMAT "\n",
+                    PRINT_UUID_ARGS(uuid));
+
+               SMC_RET2(handle, SPCI_NOT_PRESENT, 0);
+       }
+
+       /* Get lock of the array of handles */
+       spin_lock(&spci_handles_lock);
+
+       /*
+        * We need to record the client ID and Secure Partition that correspond
+        * to this handle. Look for the first free entry in the array.
+        */
+       for (i = 0; i < PLAT_SPCI_HANDLES_MAX_NUM; i++) {
+               if (spci_handles[i].status == HANDLE_STATUS_CLOSED) {
+                       break;
+               }
+       }
+
+       if (i == PLAT_SPCI_HANDLES_MAX_NUM) {
+               spin_unlock(&spci_handles_lock);
+
+               WARN("SPCI: Can't open more handles. Client 0x%04x\n",
+                    client_id);
+               WARN("SPCI:   UUID: " PRINT_UUID_FORMAT "\n",
+                    PRINT_UUID_ARGS(uuid));
+
+               SMC_RET2(handle, SPCI_NO_MEMORY, 0);
+       }
+
+       /* Create new handle value */
+       if (spci_create_handle_value(&service_handle) != 0) {
+               spin_unlock(&spci_handles_lock);
+
+               WARN("SPCI: Can't create a new handle value. Client 0x%04x\n",
+                    client_id);
+               WARN("SPCI:   UUID: " PRINT_UUID_FORMAT "\n",
+                    PRINT_UUID_ARGS(uuid));
+
+               SMC_RET2(handle, SPCI_NO_MEMORY, 0);
+       }
+
+       /* Save all information about this handle */
+       spci_handles[i].status = HANDLE_STATUS_OPEN;
+       spci_handles[i].client_id = client_id;
+       spci_handles[i].handle = service_handle;
+       spci_handles[i].num_active_requests = 0U;
+       spci_handles[i].sp_ctx = sp_ptr;
+
+       /* Release lock of the array of handles */
+       spin_unlock(&spci_handles_lock);
+
+       VERBOSE("SPCI: Service handle request by client 0x%04x: 0x%04x\n",
+               client_id, service_handle);
+       VERBOSE("SPCI:   UUID: " PRINT_UUID_FORMAT "\n", PRINT_UUID_ARGS(uuid));
+
+       /* The handle is returned in the top 16 bits of x1 */
+       SMC_RET2(handle, SPCI_SUCCESS, ((uint32_t)service_handle) << 16);
+}
+
+/*******************************************************************************
+ * This function closes a handle that a specific client uses to access a Secure
+ * Service. It returns a SPCI_*** error code.
+ ******************************************************************************/
+static uint64_t spci_service_handle_close(void *handle, u_register_t x1)
+{
+       spci_handle_t *handle_info;
+       uint16_t client_id = x1 & 0x0000FFFFU;
+       uint16_t service_handle = (x1 >> 16) & 0x0000FFFFU;
+
+       spin_lock(&spci_handles_lock);
+
+       handle_info = spci_handle_info_get(service_handle, client_id);
+
+       if (handle_info == NULL) {
+               spin_unlock(&spci_handles_lock);
+
+               WARN("SPCI: Tried to close invalid handle 0x%04x by client 0x%04x\n",
+                    service_handle, client_id);
+
+               SMC_RET1(handle, SPCI_INVALID_PARAMETER);
+       }
+
+       if (handle_info->status != HANDLE_STATUS_OPEN) {
+               spin_unlock(&spci_handles_lock);
+
+               WARN("SPCI: Tried to close handle 0x%04x by client 0x%04x in status %d\n",
+                       service_handle, client_id, handle_info->status);
+
+               SMC_RET1(handle, SPCI_INVALID_PARAMETER);
+       }
+
+       if (handle_info->num_active_requests != 0U) {
+               spin_unlock(&spci_handles_lock);
+
+               /* A handle can't be closed if there are requests left */
+               WARN("SPCI: Tried to close handle 0x%04x by client 0x%04x with %d requests left\n",
+                       service_handle, client_id,
+                       handle_info->num_active_requests);
+
+               SMC_RET1(handle, SPCI_BUSY);
+       }
+
+       memset(handle_info, 0, sizeof(spci_handle_t));
+
+       handle_info->status = HANDLE_STATUS_CLOSED;
+
+       spin_unlock(&spci_handles_lock);
+
+       VERBOSE("SPCI: Closed handle 0x%04x by client 0x%04x.\n",
+               service_handle, client_id);
+
+       SMC_RET1(handle, SPCI_SUCCESS);
+}
+
 /*******************************************************************************
  * This function handles all SMCs in the range reserved for SPCI.
  ******************************************************************************/
@@ -37,6 +267,24 @@ uint64_t spci_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2,
                case SPCI_FID_VERSION:
                        SMC_RET1(handle, SPCI_VERSION_COMPILED);
 
+               case SPCI_FID_SERVICE_HANDLE_OPEN:
+               {
+                       if ((smc_fid & SPCI_SERVICE_HANDLE_OPEN_NOTIFY_BIT) != 0) {
+                               /* Not supported for now */
+                               WARN("SPCI_SERVICE_HANDLE_OPEN_NOTIFY not supported.\n");
+                               SMC_RET1(handle, SPCI_INVALID_PARAMETER);
+                       }
+
+                       uint64_t x5 = SMC_GET_GP(handle, CTX_GPREG_X5);
+                       uint64_t x6 = SMC_GET_GP(handle, CTX_GPREG_X6);
+                       uint64_t x7 = SMC_GET_GP(handle, CTX_GPREG_X7);
+
+                       return spci_service_handle_open_poll(handle, x1, x2, x3,
+                                                            x4, x5, x6, x7);
+               }
+               case SPCI_FID_SERVICE_HANDLE_CLOSE:
+                       return spci_service_handle_close(handle, x1);
+
                default:
                        break;
                }
index e8dda0f82956982bed5dbc992b04c34c38ca3b21..ed188d48a42f97caafb353bcea1a7b5dd537780f 100644 (file)
@@ -16,6 +16,7 @@
 #include <smccc.h>
 #include <smccc_helpers.h>
 #include <spinlock.h>
+#include <string.h>
 #include <spm_svc.h>
 #include <utils.h>
 #include <xlat_tables_v2.h>
@@ -44,6 +45,38 @@ sp_context_t *spm_cpu_get_sp_ctx(unsigned int linear_id)
        return cpu_sp_ctx[linear_id];
 }
 
+/*******************************************************************************
+ * This function returns a pointer to the context of the Secure Partition that
+ * handles the service specified by an UUID. It returns NULL if the UUID wasn't
+ * found.
+ ******************************************************************************/
+sp_context_t *spm_sp_get_by_uuid(const uint32_t (*svc_uuid)[4])
+{
+       unsigned int i;
+
+       for (i = 0U; i < PLAT_SPM_MAX_PARTITIONS; i++) {
+
+               sp_context_t *sp_ctx = &sp_ctx_array[i];
+
+               if (sp_ctx->is_present == 0) {
+                       continue;
+               }
+
+               struct sp_rd_sect_service *rdsvc;
+
+               for (rdsvc = sp_ctx->rd.service; rdsvc != NULL;
+                    rdsvc = rdsvc->next) {
+                       uint32_t *rd_uuid = (uint32_t *)(rdsvc->uuid);
+
+                       if (memcmp(rd_uuid, svc_uuid, sizeof(rd_uuid)) == 0) {
+                               return sp_ctx;
+                       }
+               }
+       }
+
+       return NULL;
+}
+
 /*******************************************************************************
  * Set state of a Secure Partition context.
  ******************************************************************************/
index cfd85a3dc899c3d70c5f795df959b1d1c56a10e5..9a4e82b27dcf9625e31c0052132fa97d6d98cad3 100644 (file)
@@ -84,6 +84,7 @@ int spm_memory_attributes_set_smc_handler(sp_context_t *sp_ctx,
 /* Functions to handle Secure Partition contexts */
 void spm_cpu_set_sp_ctx(unsigned int linear_id, sp_context_t *sp_ctx);
 sp_context_t *spm_cpu_get_sp_ctx(unsigned int linear_id);
+sp_context_t *spm_sp_get_by_uuid(const uint32_t (*svc_uuid)[4]);
 
 #endif /* __ASSEMBLY__ */